import {
  Injectable,
  NotFoundException,
  BadRequestException,
  ForbiddenException,
  // ConflictException,
  Logger,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, FindOptionsWhere, MoreThan } from 'typeorm';
import { ConfigService } from '@nestjs/config';
import * as bcrypt from 'bcrypt';
// import { v4 as uuidv4 } from 'uuid';
import { Room } from './entities/room.entity';
import { Participant } from './entities/participant.entity';
import { RedisService } from '../redis/redis.service';
import {
  CreateRoomDto,
  JoinRoomDto,
  UpdateRoomDto,
  UpdateParticipantDto,
} from './dto/create-room.dto';
import {
  RoomResponseDto,
  ParticipantResponseDto,
  CreateRoomResponseDto,
  JoinRoomResponseDto,
  RoomListResponseDto,
  RoomStatsResponseDto,
} from './dto/room-response.dto';
import { plainToClass } from 'class-transformer';

@Injectable()
export class RoomsService {
  private readonly logger = new Logger(RoomsService.name);
  private readonly saltRounds = 10;

  constructor(
    @InjectRepository(Room)
    private readonly roomRepository: Repository<Room>,
    @InjectRepository(Participant)
    private readonly participantRepository: Repository<Participant>,
    private readonly redisService: RedisService,
    private readonly configService: ConfigService,
  ) {}

  // 创建房间
  async createRoom(
    createRoomDto: CreateRoomDto,
    creatorId: string,
    creatorIp: string,
  ): Promise<CreateRoomResponseDto> {
    try {
      // 检查IP限制
      await this.checkIpRateLimit(creatorIp);

      // 创建房间实体
      const room = new Room();
      room.name = createRoomDto.name;
      room.description = createRoomDto.description;
      room.creatorId = creatorId;
      room.creatorIp = creatorIp;
      room.maxParticipants = createRoomDto.maxParticipants || 2;
      room.requiresPassword = !!createRoomDto.password;
      room.settings = createRoomDto.settings || {
        audioEnabled: true,
        videoEnabled: true,
        screenShareEnabled: true,
        chatEnabled: true,
        recordingEnabled: false,
      };
      room.metadata = createRoomDto.metadata;
      room.setExpiryTime(createRoomDto.expiryMinutes || 60);

      // 如果有密码，进行哈希处理
      if (createRoomDto.password) {
        room.passwordHash = await bcrypt.hash(
          createRoomDto.password,
          this.saltRounds,
        );
      }

      // 保存到数据库
      const savedRoom = await this.roomRepository.save(room);

      // 缓存到Redis
      await this.cacheRoomData(savedRoom);

      // 生成访问令牌
      const accessToken = this.generateAccessToken(savedRoom.id, creatorId);

      // 记录IP使用情况
      await this.recordIpUsage(creatorIp);

      this.logger.log(`房间创建成功: ${savedRoom.id} by ${creatorId}`);

      const response = plainToClass(CreateRoomResponseDto, savedRoom);
      response.accessToken = accessToken;
      return response;
    } catch (error) {
      this.logger.error(`创建房间失败: ${error.message}`, error.stack);
      throw error;
    }
  }

  // 加入房间
  async joinRoom(
    joinRoomDto: JoinRoomDto,
    userId: string,
    socketId: string,
    ipAddress: string,
    userAgent?: string,
  ): Promise<JoinRoomResponseDto> {
    try {
      // 获取房间信息
      const room = await this.findRoomById(joinRoomDto.roomId);

      // 验证房间状态
      this.validateRoomForJoining(room);

      // 验证密码
      if (room.requiresPassword && joinRoomDto.password) {
        await this.validateRoomPassword(room, joinRoomDto.password);
      } else if (room.requiresPassword && !joinRoomDto.password) {
        throw new ForbiddenException('房间需要密码');
      }

      // 检查用户是否已在房间中
      const existingParticipant = await this.participantRepository.findOne({
        where: { roomId: room.id, userId, status: 'connected' },
      });

      if (existingParticipant) {
        // 重新连接
        existingParticipant.reconnect(socketId);
        const updatedParticipant =
          await this.participantRepository.save(existingParticipant);

        const accessToken = this.generateAccessToken(room.id, userId);

        return {
          room: plainToClass(RoomResponseDto, room),
          participant: plainToClass(ParticipantResponseDto, updatedParticipant),
          accessToken,
          websocketUrl: this.configService.get('app.websocket.url'),
        };
      }

      // 创建新参与者
      const participant = new Participant();
      participant.roomId = room.id;
      participant.userId = userId;
      participant.socketId = socketId;
      participant.nickname = joinRoomDto.nickname;
      participant.avatar = joinRoomDto.avatar;
      participant.ipAddress = ipAddress;
      participant.userAgent = userAgent;
      participant.role =
        room.currentParticipants === 0 ? 'host' : 'participant';
      participant.status = 'connected';
      participant.mediaDevices = joinRoomDto.mediaDevices;
      participant.metadata = joinRoomDto.metadata;

      // 保存参与者
      const savedParticipant =
        await this.participantRepository.save(participant);

      // 更新房间参与者数量
      room.addParticipant();
      await this.roomRepository.save(room);

      // 更新Redis缓存
      await this.cacheRoomData(room);
      await this.redisService.addParticipantToRoom(room.id, userId);

      // 生成访问令牌
      const accessToken = this.generateAccessToken(room.id, userId);

      this.logger.log(`用户 ${userId} 加入房间 ${room.id}`);

      return {
        room: plainToClass(RoomResponseDto, room),
        participant: plainToClass(ParticipantResponseDto, savedParticipant),
        accessToken,
        websocketUrl: this.configService.get('app.websocket.url'),
      };
    } catch (error) {
      this.logger.error(`加入房间失败: ${error.message}`, error.stack);
      throw error;
    }
  }

  // 离开房间
  async leaveRoom(roomId: string, userId: string): Promise<void> {
    try {
      const participant = await this.participantRepository.findOne({
        where: { roomId, userId, status: 'connected' },
      });

      if (!participant) {
        throw new NotFoundException('参与者不存在或已离开');
      }

      // 更新参与者状态
      participant.leave();
      await this.participantRepository.save(participant);

      // 更新房间参与者数量
      const room = await this.findRoomById(roomId);
      room.removeParticipant();
      await this.roomRepository.save(room);

      // 更新Redis缓存
      await this.redisService.removeParticipantFromRoom(roomId, userId);
      await this.cacheRoomData(room);

      this.logger.log(`用户 ${userId} 离开房间 ${roomId}`);
    } catch (error) {
      this.logger.error(`离开房间失败: ${error.message}`, error.stack);
      throw error;
    }
  }

  // 获取房间信息
  async getRoomById(
    roomId: string,
    includeParticipants = false,
  ): Promise<RoomResponseDto> {
    try {
      // 先从Redis获取
      let room = await this.redisService.getRoomData(roomId);

      if (!room) {
        // Redis中没有，从数据库获取
        const relations = includeParticipants ? ['participants'] : [];
        room = await this.roomRepository.findOne({
          where: { id: roomId },
          relations,
        });

        if (!room) {
          throw new NotFoundException('房间不存在');
        }

        // 缓存到Redis
        await this.cacheRoomData(room);
      }

      if (includeParticipants && !room.participants) {
        room.participants = await this.participantRepository.find({
          where: { roomId, status: 'connected' },
        });
      }

      return plainToClass(RoomResponseDto, room);
    } catch (error) {
      this.logger.error(`获取房间信息失败: ${error.message}`, error.stack);
      throw error;
    }
  }

  // 更新房间信息
  async updateRoom(
    roomId: string,
    updateRoomDto: UpdateRoomDto,
    userId: string,
  ): Promise<RoomResponseDto> {
    try {
      const room = await this.findRoomById(roomId);

      // 检查权限（只有创建者可以更新）
      if (room.creatorId !== userId) {
        throw new ForbiddenException('只有房间创建者可以更新房间信息');
      }

      // 更新房间信息
      Object.assign(room, updateRoomDto);
      room.updateLastActivity();

      const updatedRoom = await this.roomRepository.save(room);
      await this.cacheRoomData(updatedRoom);

      this.logger.log(`房间 ${roomId} 信息已更新`);
      return plainToClass(RoomResponseDto, updatedRoom);
    } catch (error) {
      this.logger.error(`更新房间失败: ${error.message}`, error.stack);
      throw error;
    }
  }

  // 更新参与者信息
  async updateParticipant(
    roomId: string,
    userId: string,
    updateParticipantDto: UpdateParticipantDto,
  ): Promise<ParticipantResponseDto> {
    try {
      const participant = await this.participantRepository.findOne({
        where: { roomId, userId },
      });

      if (!participant) {
        throw new NotFoundException('参与者不存在');
      }

      // 更新参与者信息
      Object.assign(participant, updateParticipantDto);
      participant.updateLastActivity();

      const updatedParticipant =
        await this.participantRepository.save(participant);

      this.logger.log(`参与者 ${userId} 信息已更新`);
      return plainToClass(ParticipantResponseDto, updatedParticipant);
    } catch (error) {
      this.logger.error(`更新参与者失败: ${error.message}`, error.stack);
      throw error;
    }
  }

  // 获取房间列表
  async getRooms(
    page = 1,
    limit = 10,
    status?: string,
    creatorId?: string,
  ): Promise<RoomListResponseDto> {
    try {
      const where: FindOptionsWhere<Room> = {};

      if (status) {
        where.status = status as any;
      }

      if (creatorId) {
        where.creatorId = creatorId;
      }

      // 只显示未过期的房间
      where.expiresAt = MoreThan(new Date());

      const [rooms, total] = await this.roomRepository.findAndCount({
        where,
        order: { createdAt: 'DESC' },
        skip: (page - 1) * limit,
        take: limit,
      });

      return {
        rooms: rooms.map(room => plainToClass(RoomResponseDto, room)),
        total,
        page,
        limit,
        totalPages: Math.ceil(total / limit),
        hasNext: page < Math.ceil(total / limit),
        hasPrev: page > 1,
      };
    } catch (error) {
      this.logger.error(`获取房间列表失败: ${error.message}`, error.stack);
      throw error;
    }
  }

  // 获取房间统计信息
  async getRoomStats(): Promise<RoomStatsResponseDto> {
    try {
      const [totalRooms, activeRooms, waitingRooms, endedRooms] =
        await Promise.all([
          this.roomRepository.count(),
          this.roomRepository.count({ where: { status: 'active' } }),
          this.roomRepository.count({ where: { status: 'waiting' } }),
          this.roomRepository.count({ where: { status: 'ended' } }),
        ]);

      const [totalParticipants, onlineParticipants] = await Promise.all([
        this.participantRepository.count(),
        this.participantRepository.count({ where: { status: 'connected' } }),
      ]);

      // 计算平均房间持续时间（简化计算）
      const averageRoomDuration = 30; // 临时值，实际应该从数据库计算

      return {
        totalRooms,
        activeRooms,
        waitingRooms,
        endedRooms,
        totalParticipants,
        onlineParticipants,
        averageRoomDuration,
        timestamp: new Date(),
      };
    } catch (error) {
      this.logger.error(`获取统计信息失败: ${error.message}`, error.stack);
      throw error;
    }
  }

  // 删除房间
  async deleteRoom(roomId: string, userId: string): Promise<void> {
    try {
      const room = await this.findRoomById(roomId);

      // 检查权限
      if (room.creatorId !== userId) {
        throw new ForbiddenException('只有房间创建者可以删除房间');
      }

      // 删除所有参与者
      await this.participantRepository.delete({ roomId });

      // 删除房间
      await this.roomRepository.delete(roomId);

      // 清除Redis缓存
      await this.redisService.deleteRoom(roomId);

      this.logger.log(`房间 ${roomId} 已删除`);
    } catch (error) {
      this.logger.error(`删除房间失败: ${error.message}`, error.stack);
      throw error;
    }
  }

  // 清理过期房间
  async cleanupExpiredRooms(): Promise<number> {
    try {
      const expiredRooms = await this.roomRepository.find({
        where: {
          expiresAt: MoreThan(new Date()),
        },
      });

      let cleanedCount = 0;
      for (const room of expiredRooms) {
        await this.participantRepository.delete({ roomId: room.id });
        await this.roomRepository.delete(room.id);
        await this.redisService.deleteRoom(room.id);
        cleanedCount++;
      }

      this.logger.log(`清理了 ${cleanedCount} 个过期房间`);
      return cleanedCount;
    } catch (error) {
      this.logger.error(`清理过期房间失败: ${error.message}`, error.stack);
      throw error;
    }
  }

  // 私有方法
  private async findRoomById(roomId: string): Promise<Room> {
    const room = await this.roomRepository.findOne({ where: { id: roomId } });
    if (!room) {
      throw new NotFoundException('房间不存在');
    }
    return room;
  }

  private validateRoomForJoining(room: Room): void {
    if (room.isExpired) {
      throw new BadRequestException('房间已过期');
    }
    if (room.isFull) {
      throw new BadRequestException('房间已满');
    }
    if (room.status === 'ended') {
      throw new BadRequestException('房间已结束');
    }
  }

  private async validateRoomPassword(
    room: Room,
    password: string,
  ): Promise<void> {
    if (!room.passwordHash) {
      return;
    }
    const isValid = await bcrypt.compare(password, room.passwordHash);
    if (!isValid) {
      throw new ForbiddenException('房间密码错误');
    }
  }

  private generateAccessToken(roomId: string, userId: string): string {
    // 简化的令牌生成，实际应该使用JWT
    return Buffer.from(`${roomId}:${userId}:${Date.now()}`).toString('base64');
  }

  private async cacheRoomData(room: Room): Promise<void> {
    const ttl = Math.floor((room.expiresAt.getTime() - Date.now()) / 1000);
    if (ttl > 0) {
      await this.redisService.setRoomData(room.id, room, ttl);
    }
  }

  private async checkIpRateLimit(ip: string): Promise<void> {
    const key = `rate_limit:${ip}`;
    const count = await this.redisService.getCounter(key);
    const limit = this.configService.get('app.room.maxRoomsPerIp', 5);

    if (count >= limit) {
      throw new BadRequestException('IP创建房间数量超限');
    }
  }

  private async recordIpUsage(ip: string): Promise<void> {
    const key = `rate_limit:${ip}`;
    const ttl = 3600; // 1小时
    await this.redisService.incrementCounter(key, ttl);
  }
}
